package com.fsoft.core.utils.excel;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.RoundingMode;
import java.net.HttpURLConnection;
import java.net.URL;
import java.sql.SQLException;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections4.map.CaseInsensitiveMap;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.util.UriUtils;

import com.fsoft.core.Global;
import com.fsoft.core.common.base.entity.UserVo;
import com.fsoft.core.context.FSContext;
import com.fsoft.core.utils.DateTimeUtils;
import com.fsoft.core.utils.OgnlUtils;
import com.fsoft.core.utils.SqlExecutor;
import com.fsoft.core.utils.qrcode.QrCodeUtil;
import com.google.zxing.WriterException;

/***
 * 执行脚本公共函数
 * @package com.fsoft.core.utils.excel
 * @author fish
 * @email it.fish2010@foxmail.com
 * @date 2021-09-30
 * @desc
 *
 */
@Component
public class ScriptContext extends CaseInsensitiveMap<String, Object> {
	/**
	 * 
	 */
	private static final long serialVersionUID = 933747753127097596L;
	private JdbcTemplate jdbcTemplate;
	private SqlExecutor sqlExecutor;
	private static String dbtype = null;
	public DatabaseIdProvider databaseIdProvider;

	public ScriptContext() {
		super();
		this.initDefaultValue();
	}

	private void initDefaultValue() {
		UserVo user = FSContext.getInstance().getUser();
		if (null == user) {
			return;
		}
		this.put(Global.F_USERID, user.getId());
		this.put("loginAcct", user.getLoginAcct());
		this.put("name", user.getName());
		this.put(Global.F_ROLEID, user.getRoleId());
		this.put(Global.F_DEPTID, user.getDeptId());
		this.put("deptName", user.getDeptName());
		this.put(Global.F_ORGID, user.getOrgId());
		this.put("orgName", user.getOrgName());
	}

	public String getUserId() {
		return (String) this.get(Global.F_USERID);
	}

	public String getDeptId() {
		return (String) this.get(Global.F_DEPTID);
	}

	public String getOrgId() {
		return (String) this.get(Global.F_ORGID);
	}

	public String getRoleId() {
		return (String) this.get(Global.F_ROLEID);
	}

	public String getLoginAcct() {
		return (String) this.get("loginAcct");
	}

	public boolean isBank(String value) {
		return StringUtils.isBlank(value);
	}

	/**
	 * 判断对象是否为空
	 * @param value
	 * @return
	 * @author qjb
	 * @date 2020年4月2日 下午3:17:18
	 */
	public boolean isBank(Object value) {
		return OgnlUtils.isEmpty(value);
	}

	/**
	 * 
	 * 是否数字
	 * 
	 * @param value
	 * @return
	 * @author qjb
	 * @date 2019年3月1日 下午1:45:52
	 */
	public boolean isNumber(String value) {
		try {
			Double.valueOf(value);
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	/**
	 * 转为数字
	 * 
	 * @param value
	 * @return
	 * @author qjb
	 * @date 2019年3月1日 下午2:00:53
	 */
	public Double toNumber(String value) {
		return Double.valueOf(value);
	}

	/**
	 * 字符串以prefix为开头
	 * 
	 * @param source
	 * @param prefix
	 * @return
	 * @author qjb
	 * @date 2019年3月1日 下午2:17:57
	 */
	public boolean startWith(String source, String prefix) {
		return source.startsWith(prefix);
	}

	/**
	 * 字符串以suffix结尾
	 * 
	 * @param source
	 * @param suffix
	 * @return
	 * @author qjb
	 * @date 2019年3月1日 下午2:19:06
	 */
	public boolean endWith(String source, String suffix) {
		return source.endsWith(suffix);
	}

	/**
	 * 字符串包涵str的位置
	 * 
	 * @param source
	 * @param str
	 * @return
	 * @author qjb
	 * @date 2019年3月1日 下午2:21:40
	 */
	public int indexOf(String source, String str) {
		return source.indexOf(str);
	}

	/**
	 * 字符串是否包涵某个字符
	 * 
	 * @param source
	 * @param str
	 * @return
	 * @author qjb
	 * @date 2019年3月1日 下午2:25:02
	 */
	public boolean contains(String source, String str) {
		return source.contains(str);
	}

	/**
	 * 字符串转大写
	 * 
	 * @param source
	 * @return
	 * @author qjb
	 * @date 2019年3月1日 下午2:27:07
	 */
	public String toUpper(String source) {
		return source.toUpperCase();
	}

	/**
	 * 字符串转小写
	 * 
	 * @param source
	 * @return
	 * @author qjb
	 * @date 2019年3月1日 下午2:28:27
	 */
	public String toLow(String source) {
		return source.toLowerCase();
	}

	/**
	 * 字符串是否等于某个字符串
	 * 
	 * @param source
	 * @param tagSource
	 * @return
	 * @author qjb
	 * @date 2019年3月1日 下午2:29:48
	 */
	public boolean equals(String source, String tagSource) {
		return source.equals(tagSource);
	}

	/**
	 * 字符串是否等于某个字符串，忽略大小写
	 * 
	 * @param source
	 * @param tagSource
	 * @return
	 * @author qjb
	 * @date 2019年3月1日 下午2:31:22
	 */
	public boolean equalsIgnoreCase(String source, String tagSource) {
		return source.equalsIgnoreCase(tagSource);
	}

	public String getGuid() {
		return OgnlUtils.randomUpperCaseId();
	}

	/**
	 * 某个字符串的长度
	 * @param source
	 * @return
	 * @author qjb
	 * @date 2019年5月17日 下午1:40:34
	 */
	public int length(String source) {
		if (StringUtils.isBlank(source)) {
			return 0;
		} else {
			return source.length();
		}
	}

	/**
	 * 去掉字符串的空格
	 * 
	 * @param source
	 * @return
	 * @author qjb
	 * @date 2019年5月17日 下午1:44:53
	 */
	public String trim(String source) {
		if (StringUtils.isBlank(source)) {
			return null;
		} else {
			return source.trim();
		}
	}

	/**
	 * 某个字符串把xx替换成yy,支持正则表达式
	 * 
	 * @param source
	 * @param regx
	 * @param tag
	 * @return
	 * @author qjb
	 * @date 2019年5月17日 下午2:47:53
	 */
	public String replaceAll(String source, String regx, String tag) {
		if (StringUtils.isBlank(source)) {
			return null;
		}
		return source.replaceAll(regx, tag);
	}

	/**
	 * 保留小数,格式为千分号
	 * 
	 * @param sourec
	 * @param length 保留长度
	 * @return
	 * @author qjb
	 * @date 2019年5月17日 上午10:44:54
	 */
	public String format(String source, int length, boolean showPermil) {
		double temp;
		try {
			temp = Double.valueOf(source);
		} catch (Exception e) {
			return source;
		}
		NumberFormat nf = NumberFormat.getInstance();
		nf.setMinimumFractionDigits(length);
		nf.setMaximumFractionDigits(length);
		nf.setGroupingUsed(showPermil);
		nf.setRoundingMode(RoundingMode.HALF_UP);
		return nf.format(temp);
	}

	/**
	 * 求和
	 * 
	 * @param object
	 * @return
	 * @author qjb
	 * @date 2019年5月13日 下午1:49:32
	 */
	public double sum(double[] object) {
		double sum = 0;
		for (double temp : object) {
			sum += temp;
		}
		return sum;
	}

	/**
	 * 平均值
	 * 
	 * @param object
	 * @return
	 * @author qjb
	 * @date 2019年5月13日 下午2:18:11
	 */
	public double avg(double[] object) {
		double sum = 0;
		int index = 0;
		if (object.length == 0) {
			return 0;
		}
		for (double temp : object) {
			sum += temp;
			index++;
		}
		double returnValue = sum / index;

		DecimalFormat df = new DecimalFormat("#.00");
		return Double.parseDouble(df.format(returnValue));
	}

	/**
	 * 查询最大值
	 * 
	 * @param object
	 * @return
	 * @author qjb
	 * @date 2019年5月13日 下午2:45:35
	 */
	public double max(double[] object) {
		double temp = 0;
		if (object.length == 0) {
			return temp;
		}
		temp = object[0];
		for (double tempobject : object) {
			if (tempobject > temp) {
				temp = tempobject;
			}
		}
		return temp;
	}

	/**
	 * 获取最小值
	 * 
	 * @param object
	 * @return
	 * @author qjb
	 * @date 2019年5月13日 下午2:49:01
	 */
	public double min(double[] object) {
		double temp = 0;
		if (object.length == 0) {
			return temp;
		}
		temp = object[0];
		for (double tempobject : object) {
			if (tempobject < temp) {
				temp = tempobject;
			}
		}
		return temp;
	}

	public double product(double[] object) {
		double total = 0;
		if (object.length == 0) {
			return total;
		}
		total = object[0];
		for (int i = 1; i <= object.length - 1; i++) {
			total = total * object[i];
		}
		return total;
	}

	/**
	 * ifValue表达式，如果true，返回第一个参数，否则返回第二个参数
	 * @author fsoft
	 * @date 2021-09-30
	 * @param b
	 * @param trueValue
	 * @param falseValue
	 * @return
	 */
	public Object ifValue(boolean b, Object trueValue, Object falseValue) {
		if (b) {
			return trueValue;
		} else {
			return falseValue;
		}
	}

	/**
	 * and逻辑关系运算符，boolean 数组，只要有一个false则返回false
	 * @author fsoft
	 * @date 2021-09-30
	 * @param args
	 * @return
	 */
	public boolean and(boolean[] args) {
		if (args.length == 0) {
			return false;
		}
		for (boolean temp : args) {
			if (!temp) {
				return false;
			}
		}
		return true;

	}

	/**
	 * or逻辑关系运算符，boolean 数组，只要有一个true则返回true
	 * 
	 * @param args
	 * @return
	 * @author qjb
	 * @date 2019年5月17日 下午2:19:34
	 */
	public boolean or(boolean[] args) {
		if (args.length == 0) {
			return false;
		}
		for (boolean temp : args) {
			if (temp) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 返回第一个非空的值
	 * @param arg
	 * @return
	 * @author qjb
	 * @date 2019年5月17日 上午11:52:27
	 */
	public Object nvl(Object[] arg) {
		for (Object temp : arg) {
			if (!OgnlUtils.isEmpty(temp)) {
				return temp;
			}
		}
		return null;
	}

	/**
	 * 返回当前时间字符串，yyyy-MM-dd格式
	 * 
	 * @return
	 * @author qjb
	 * @date 2019年5月17日 下午1:51:49
	 */
	public String date() {
		SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
		return df.format(new Date());
	}

	/**
	 * 返回当前时间字符串，yyyy-MM-dd HH:mm:ss格式
	 * 
	 * @return
	 * @author qjb
	 * @date 2019年5月17日 下午1:53:26
	 */
	public String datetime() {
		SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		return df.format(new Date());
	}

	/**
	 * 获取当前时间，返回时间类型
	 * 
	 * @return
	 * @author qjb
	 * @date 2019年5月17日 下午2:05:19
	 */
	public Date now() {
		return new Date();
	}

	public String dateFormat(Date date, String format) {
		return DateTimeUtils.format(date, format);

	}

	/***
	 * 执行查询sql，返回list
	 * @author fsoft
	 * @date 2021-09-30
	 * @param sql
	 * @return
	 */
	public Object exceSelectSql(String sql) {
		if (sqlExecutor == null) {
			sqlExecutor = FSContext.getInstance().getBean(SqlExecutor.class);
		}
		Object obj = sqlExecutor.selectList(sql, Map.class);
		return obj;
	}

	/***
	 * 执行查询sql
	 * @author fsoft
	 * @date 2021-09-30
	 * @param sql      SQL语句
	 * @param variants SQL的参数
	 * @return
	 */
	public Object exceSelectSql(String sql, Object variants) {
		if (sqlExecutor == null) {
			sqlExecutor = FSContext.getInstance().getBean(SqlExecutor.class);
		}
		Object obj = sqlExecutor.selectList(sql, variants);
		return obj;
	}

	/**
	 * 关联两个List<Map<String, Object> 用于主从表 关联 建立以下的结构: master ->[detailAlias]=List<Map<String, Object>> details ->......
	 * 
	 * @author fsoft
	 * @date 2021-09-30
	 * @param masters     主表数据
	 * @param primaryKey  主表-主键
	 * @param details     从表数据
	 * @param relateKey   从表-外键
	 * @param detailAlias 从表的别名
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public void masterDetial(List<Map<String, Object>> masters, String primaryKey, List<Map<String, Object>> details, String relateKey, String detailAlias) throws Exception {

		for (Map<String, Object> detailRow : details) {
			if (!detailRow.containsKey(relateKey)) {
				throw new Exception("masterDetial：不存在的外键【" + relateKey + "】，检查details的是否包含有该键值。");
			}
			String fkValue = (String) detailRow.get(relateKey);
			if (StringUtils.isBlank(fkValue)) {
				throw new Exception("masterDetial：关联数据时候，存在外键为空的数据");
			}
			// 查找主表的数据
			Map<String, Object> m = findMasterList(masters, primaryKey, fkValue);
			if (m != null) {
				if (!m.containsKey(detailAlias)) {
					m.put(detailAlias, new ArrayList<Map<String, Object>>());
				} else {
					if (!(m.get(detailAlias) instanceof List)) {
						throw new Exception("从表数据被包含有非List类型的数据，请修改detailAlias 名");
					}
				}
				ArrayList<Map<String, Object>> childs = (ArrayList<Map<String, Object>>) m.get(detailAlias);
				childs.add(detailRow);
			}
		}
		for (Map<String, Object> row : masters) {
			if (!row.containsKey(detailAlias)) {
				row.put(detailAlias, new ArrayList<Map<String, Object>>());
			}
		}
	}

	private Map<String, Object> findMasterList(List<Map<String, Object>> masters, String primaryKey, String primaryKeyValue) {
		for (Map<String, Object> r : masters) {
			if (primaryKeyValue.equals(r.get(primaryKey))) {
				return r;
			}
		}
		return null;
	}

	/**
	 * 生成二维码图片
	 * 
	 * @author gk @date 2019年10月9日 上午9:38:46
	 * @param str         需要生成二维码的字符串
	 * @param qrCodeSize  二维码大小
	 * @param imageFormat 二维码的图片格式
	 * @return 图片byte[]
	 * @throws WriterException
	 * @throws IOException
	 */
	public byte[] CreateQrCode(String str, int qrCodeSize, String imageFormat) throws WriterException, IOException {
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		QrCodeUtil.createQrCode(out, str, qrCodeSize, imageFormat);
		return out.toByteArray();
	}

	private final String DEFAULT_CHARSET = "utf-8";
	private final int DEFAULT_TIME_OUT = 300000;

	/**
	 * 通过url获取图片的byte[]
	 * 
	 * @author gk @date 2019年10月9日 上午9:54:39
	 * @param url 图片url
	 * @return 成功返回图片的数组 否则返回空
	 * @throws WriterException
	 * @throws IOException
	 */
	public byte[] getImageFromUrl(String urlStr) throws WriterException, IOException {
		InputStream is = null;
		ByteArrayOutputStream os = null;
		byte[] buff = new byte[1024];
		int len = 0;
		try {
			URL url = new URL(UriUtils.encodePath(urlStr, DEFAULT_CHARSET));
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setRequestProperty("Content-Type", "plain/text;charset=" + DEFAULT_CHARSET);
			conn.setRequestProperty("charset", DEFAULT_CHARSET);
			conn.setDoInput(true);
			conn.setDoOutput(true);
			conn.setRequestMethod("GET");
			conn.setReadTimeout(DEFAULT_TIME_OUT);
			conn.connect();
			is = conn.getInputStream();
			os = new ByteArrayOutputStream();
			while ((len = is.read(buff)) != -1) {
				os.write(buff, 0, len);
			}
			return os.toByteArray();
		} catch (IOException e) {
			return null;
		} finally {
			if (is != null) {
				try {
					is.close();
				} catch (IOException e) {
				}
			}
			if (os != null) {
				try {
					os.close();
				} catch (IOException e) {
				}
			}
		}
	}

	/**
	 * 执行查询总数sql
	 * 
	 * @param sql
	 * @return
	 * @author qjb
	 * @date 2019年5月29日 上午9:57:15
	 */
	public Integer exceTotalSql(String sql) {
		if (sqlExecutor == null) {
			sqlExecutor = FSContext.getInstance().getBean(SqlExecutor.class);
		}
		Integer obj = (Integer) sqlExecutor.selectOneResult(sql, Integer.class);
		return obj;
	}

	/**
	 * 执行查询sql,返回List<Integer>
	 * 
	 * @param sql
	 * @return
	 * @author qjb
	 * @date 2019年5月29日 上午9:57:15
	 */
	public Object exceSelectIntSql(String sql) {
		if (sqlExecutor == null) {
			sqlExecutor = FSContext.getInstance().getBean(SqlExecutor.class);
		}
		Object obj = sqlExecutor.selectList(sql, Integer.class);
		return obj;
	}

	/***
	 * 返回当前的数据库类型
	 * @author fsoft
	 * @date 2021-09-30
	 * @return
	 */
	public String getDbType() {
		if (dbtype == null) {
			try {
				if (databaseIdProvider == null)
					databaseIdProvider = FSContext.getInstance().getBean(DatabaseIdProvider.class);
				dbtype = databaseIdProvider.getDatabaseId(this.jdbcTemplate.getDataSource());
			} catch (SQLException e) {
				return null;
			}
		}
		return dbtype;
	}

	/**
	 * 执行sql
	 * @author fsoft
	 * @date 2021-09-30
	 * @param sql
	 * @param params
	 */
	public void excelSql(String sql, Object params) {
		if (sqlExecutor == null) {
			sqlExecutor = FSContext.getInstance().getBean(SqlExecutor.class);
		}
		sqlExecutor.execSql(sql, params);
	}

	/**
	 * 执行sql
	 * @author fsoft
	 * @date 2021-09-30
	 * @param sql
	 */
	public void excelSql(String sql) {
		if (jdbcTemplate == null) {
			jdbcTemplate = FSContext.getInstance().getBean(JdbcTemplate.class);
		}
		jdbcTemplate.execute(sql);
	}

	/***
	 * 批量执行sql
	 * @author fsoft
	 * @date 2021-09-30
	 * @param sqlList
	 */
	public void excelSqlBatch(String[] sqlList) {
		if (jdbcTemplate == null) {
			jdbcTemplate = FSContext.getInstance().getBean(JdbcTemplate.class);
		}
		jdbcTemplate.batchUpdate(sqlList);
	}

}
